#pragma once
//实现文件存储子服务
//1.实现文件rpc服务类 --- 实现rpc调用的业务处理接口
//2.实现文件存储子服务的服务类
//3.实现文件存储子服务类建造者

#include <brpc/server.h>
#include <butil/logging.h>
#include "logger.hpp"  //日志模块封装
#include "etcd.hpp"    //服务注册模块封装
#include "utils.hpp"
#include "base.pb.h"
#include "file.pb.h"

namespace zrb
{
    // （1）.创建rpc服务子类继承pb中的FileService服务类，并实现内部的业务接口逻辑
    class FileServiceImpl : public zrb::FileService
    {
    public:
        FileServiceImpl(const std::string& storage_path)
            :_storage_path(storage_path)
        {
            umask(0);
            mkdir(_storage_path.c_str(),0775); //创建目录
            if(_storage_path.back() != '/') //末尾字符不是'/'，就进行加上'/'
            {
                _storage_path.push_back('/');
            }
        }

        void GetSingleFile(google::protobuf::RpcController *controller,
            const ::zrb::GetSingleFileReq *request,
            ::zrb::GetSingleFileRsp *response,
            ::google::protobuf::Closure *done)
        {
            try
            {
                brpc::ClosureGuard rpc_guard(done);
                response->set_request_id(request->request_id());
                //1.取出文件的请求id，起始就是文件名，再加上默认路径
                std::string fileid = request->file_id();
                std::string filename = _storage_path + fileid;

                LOG_DEBUG("文件id的长度{} 文件名的长度{}",fileid.size(),filename.size());

                std::cout << "正常运行111" << std::endl;
                //2.读取文件的数据
                std::string body;
                bool ret = readFile(filename,body);
                if(ret == false)
                {
                    LOG_ERROR("下载{}文件读取失败",filename);
                    response->set_success(false);
                    response->set_errmsg("下载文件读取失败");
                    return;
                }
                std::cout << "正常运行222" << std::endl;
                //3.组织响应
                response->set_success(true);
                response->mutable_file_data()->set_file_id(fileid);
                response->mutable_file_data()->set_file_content(body);
            }
            catch(std::exception& e)
            {
                std::cout << e.what() << std::endl;
            }
        }

        void GetMultiFile(google::protobuf::RpcController *controller,
            const ::zrb::GetMultiFileReq *request,
            ::zrb::GetMultiFileRsp *response,
            ::google::protobuf::Closure *done)
        {
            brpc::ClosureGuard rpc_guard(done);
            response->set_request_id(request->request_id());
            //循环取出请求中的文件ID，读取文件数据进行填充
            for(int i = 0; i < request->file_id_list_size(); ++i)
            {
                //1.取出文件的请求id，起始就是文件名
                std::string fileid = request->file_id_list(i); //读取到第i个文件
                std::string filename = _storage_path + fileid;
                //2.读取文件的数据
                std::string body;
                bool ret = readFile(filename,body);
                if(ret == false) //其中一个读取失败 整个读取就失败
                {
                    LOG_ERROR("下载{}文件读取失败",filename);
                    response->set_success(false);
                    response->set_errmsg("下载文件读取失败");
                    return;
                }
                //3.组织响应
                response->set_success(true);

                FileDownloadData data;
                data.set_file_id(fileid);
                data.set_file_content(body);

                //添加到列表里面
                //mutable前缀是获取到该变量的指针
                response->mutable_file_data()->insert({fileid,data});
            }
            response->set_success(true);
        }
        

        void PutSingleFile(google::protobuf::RpcController *controller,
            const ::zrb::PutSingleFileReq *request,
            ::zrb::PutSingleFileRsp *response,
            ::google::protobuf::Closure *done)
        {
            brpc::ClosureGuard rpc_guard(done);
            response->set_request_id(request->request_id());
            //1.为文件生成一个唯一uuid作为文件名以及文件ID 再加上默认路径
            std::string fid = uuid();
            std::string filename = _storage_path + fid;
            //2.取出请求中的文件数据，进行数据写入
            bool ret = writeFile(filename,request->file_data().file_content());
            if(ret == false)
            {
                LOG_ERROR("上传{}文件失败",request->file_data().file_name());
                response->set_success(false);
                // response->set_errmsg("上传{}文件失败",request->file_data().file_name());
                response->set_errmsg("上传文件失败");
                return;
            }
            //3.组织响应
            response->mutable_file_info()->set_file_id(fid);
            response->mutable_file_info()->set_file_name(request->file_data().file_name());
            response->mutable_file_info()->set_file_size(request->file_data().file_size());

            response->set_success(true);
        }

        void PutMultiFile(google::protobuf::RpcController *controller,
            const ::zrb::PutMultiFileReq *request,
            ::zrb::PutMultiFileRsp *response,
            ::google::protobuf::Closure *done)
        {
            brpc::ClosureGuard rpc_guard(done);
            response->set_request_id(request->request_id());
            //相对单个文件的操作 加了个循环
            for(int i = 0; i < request->file_data_size(); ++i)
            {
                //1.为文件生成一个唯一uuid作为文件名以及文件ID
                std::string fid = uuid();
                std::string filename = _storage_path + fid;
                //2.取出请求中的文件数据，进行数据写入
                bool ret = writeFile(filename,request->file_data(i).file_content());
                if(ret == false)
                {
                    LOG_ERROR("上传{}文件读取失败",request->file_data(i).file_name());
                    response->set_success(false);
                    // response->set_errmsg("上传{}文件读取失败",request->file_data(i).file_name());
                    response->set_errmsg("上传文件失败");
                    return;
                }
                //3.组织响应
                FileMessageInfo* info = response->add_file_info();
                info->set_file_id(fid);
                info->set_file_name(request->file_data(i).file_name());
                info->set_file_size(request->file_data(i).file_size());

                response->set_success(true);
            }
            response->set_success(true);
        }

        ~FileServiceImpl()
        {}

    private:
        std::string _storage_path; //上传文件的存储路径
    };

    // （2）.创建rpc服务器类，搭建服务器
    //  （3）.向服务器中添加rpc子服务对象 -- 告诉服务器收到什么请求用哪个接口处理
    class FileServer
    {
    public:
        using ptr = shared_ptr<FileServer>;
        FileServer(const Registry::ptr& rclient,const std::shared_ptr<brpc::Server>& rpc_server)
            :_rclient(rclient) 
            ,_rpc_server(rpc_server)
        {
        }

        void start()
        {
            _rpc_server->RunUntilAskedToQuit();//休眠等待运行结束 
        }

        ~FileServer()
        {
        }

    private:
        std::shared_ptr<brpc::Server> _rpc_server;       // brpc服务器对象
        Registry::ptr _rclient;                          // etcd服务器注册类
    };

    //FileService构造过程参数太多，使用建造者模式
    class FileServerBuild
    {
    public:
        // 构造etcd服务注册对象
        // 网络Ip、注册的服务名称、注册的服务地址
        void make_reg_object(const std::string& host,const std::string& name,const std::string &access_host) 
        {
            _rclient = std::make_shared<Registry>(host);
            _rclient->registry(name,access_host); //服务注册
        }
        // 构造brpc服务对象
        // 端口 超时时间 线程数量
        void make_rpc_object(uint16_t port, uint32_t timeout, uint8_t thread_num,const std::string& path = "./data/")
        {
            _rpc_server = std::make_shared<brpc::Server>();

            //向服务器增加一个SpeechServiceImpl服务，SpeechServiceImpl服务是自己实现的
            FileServiceImpl* echo_service = new FileServiceImpl(path);
            //brpc::ServiceOwnership::SERVER_DOESNT_OWN_SERVICE 表示添加服务失败，服务器也不会删除服务对象
            //brpc::ServiceOwnership::SERVER_OWNS_SERVICE 表示添加服务失败时，服务器负责删除服务对象
            int ret = _rpc_server->AddService(echo_service, brpc::ServiceOwnership::SERVER_OWNS_SERVICE);
            if (ret == -1) {
                LOG_ERROR("添加Rpc服务失败！");
                abort();
            }

            //启动服务器
            brpc::ServerOptions options;
            options.idle_timeout_sec = timeout; //连接空闲超时时间-超时后连接被关闭
            options.num_threads = thread_num; // io线程数量
            ret = _rpc_server->Start(port, &options); //监听的端口
            if (ret == -1) {
                LOG_ERROR("rpc服务器启动失败");
                abort();
            }
        }
   
        FileServer::ptr build()
        {
            if(!_rpc_server)
            {
                LOG_ERROR("还未初始化rpc服务模块");
                abort();
            }
            if(!_rclient)
            {
                LOG_ERROR("还未初始化服务注册客户端模块");
                abort();
            }
            FileServer::ptr server = std::make_shared<FileServer>(_rclient,_rpc_server);
            return server;
        }

    private:
        std::shared_ptr<brpc::Server> _rpc_server;       // brpc服务器对象
        Registry::ptr _rclient;                          // etcd服务器注册类
    };
}

